﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DDD;
using Xunit;

using Vector4 = DDD.Vector4<float>;


namespace DokiDokiTest {
    public class TestMatrix4x4 {
        [Fact]
        public void Test_New1 () {
            var m = new Matrix4x4 ();

            Assert.Equal (16, m.ComponentCount);
            Assert.Equal (0, m.M00);
            Assert.Equal (0, m.M01);
            Assert.Equal (0, m.M02);
            Assert.Equal (0, m.M03);
            Assert.Equal (0, m.M10);
            Assert.Equal (0, m.M11);
            Assert.Equal (0, m.M12);
            Assert.Equal (0, m.M13);
            Assert.Equal (0, m.M20);
            Assert.Equal (0, m.M21);
            Assert.Equal (0, m.M22);
            Assert.Equal (0, m.M23);
            Assert.Equal (0, m.M30);
            Assert.Equal (0, m.M31);
            Assert.Equal (0, m.M32);
            Assert.Equal (0, m.M33);
            Assert.Equal (0, m[0]);
            Assert.Equal (0, m[1]);
            Assert.Equal (0, m[2]);
            Assert.Equal (0, m[3]);
            Assert.Equal (0, m[4]);
            Assert.Equal (0, m[5]);
            Assert.Equal (0, m[6]);
            Assert.Equal (0, m[7]);
            Assert.Equal (0, m[8]);
            Assert.Equal (0, m[9]);
            Assert.Equal (0, m[10]);
            Assert.Equal (0, m[11]);
            Assert.Equal (0, m[12]);
            Assert.Equal (0, m[13]);
            Assert.Equal (0, m[14]);
            Assert.Equal (0, m[15]);
        }

        [Fact]
        public void Test_New2 () {
            var m = new Matrix4x4 (1, 2, 3, 4,
                                   5, 6, 7, 8,
                                   9, 10, 11, 12,
                                   13, 14, 15, 16);

            Assert.Equal (16, m.ComponentCount);
            Assert.Equal (1, m.M00);
            Assert.Equal (2, m.M01);
            Assert.Equal (3, m.M02);
            Assert.Equal (4, m.M03);
            Assert.Equal (5, m.M10);
            Assert.Equal (6, m.M11);
            Assert.Equal (7, m.M12);
            Assert.Equal (8, m.M13);
            Assert.Equal (9, m.M20);
            Assert.Equal (10, m.M21);
            Assert.Equal (11, m.M22);
            Assert.Equal (12, m.M23);
            Assert.Equal (13, m.M30);
            Assert.Equal (14, m.M31);
            Assert.Equal (15, m.M32);
            Assert.Equal (16, m.M33);
            Assert.Equal (1, m[0]);
            Assert.Equal (2, m[1]);
            Assert.Equal (3, m[2]);
            Assert.Equal (4, m[3]);
            Assert.Equal (5, m[4]);
            Assert.Equal (6, m[5]);
            Assert.Equal (7, m[6]);
            Assert.Equal (8, m[7]);
            Assert.Equal (9, m[8]);
            Assert.Equal (10, m[9]);
            Assert.Equal (11, m[10]);
            Assert.Equal (12, m[11]);
            Assert.Equal (13, m[12]);
            Assert.Equal (14, m[13]);
            Assert.Equal (15, m[14]);
            Assert.Equal (16, m[15]);
        }

        [Fact]
        public void Test_New3 () {
            var m = new Matrix4x4 (new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 });
            var expect = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };

            Assert.Equal (expect, (float[])m);

        }



        [Fact]
        public void Test_Indexer () {
            var m = new Matrix4x4 ();
            m[0] = 1;
            m[1] = 2;
            m[2] = 3;
            m[3] = 4;
            m[4] = 5;
            m[5] = 6;
            m[6] = 7;
            m[7] = 8;
            m[8] = 9;
            m[9] = 10;
            m[10] = 11;
            m[11] = 12;
            m[12] = 13;
            m[13] = 14;
            m[14] = 15;
            m[15] = 16;
            Assert.Equal (1, m[0]);
            Assert.Equal (2, m[1]);
            Assert.Equal (3, m[2]);
            Assert.Equal (4, m[3]);
            Assert.Equal (5, m[4]);
            Assert.Equal (6, m[5]);
            Assert.Equal (7, m[6]);
            Assert.Equal (8, m[7]);
            Assert.Equal (9, m[8]);
            Assert.Equal (10, m[9]);
            Assert.Equal (11, m[10]);
            Assert.Equal (12, m[11]);
            Assert.Equal (13, m[12]);
            Assert.Equal (14, m[13]);
            Assert.Equal (15, m[14]);
        }


        [Fact]
        public void Test_Determinant () {
            var m = new Matrix4x4 (1, 2, 3, 0,
                                   1, 0, -1, 1,
                                   2, 1, 1, 0,
                                   1, 1, 1, 1);
            Assert.Equal (-1, m.Deternimant);
        }

        [Fact]
        public void Test_Matrix3x3 () {
            var m4x4 = new Matrix4x4 (1, 2, 3, 4,
                                      5, 6, 7, 8,
                                      9, 10, 11, 12,
                                      13, 14, 15, 16);
            var m3x3 = new Matrix3x3 (1, 2, 3,
                                     5, 6, 7,
                                     9, 10, 11);
            Assert.Equal (m3x3, m4x4.Matrix3x3);
        }

        [Fact]
        public void Test_Ideneity () {
            var m = Matrix4x4.Identity;
            var expect = new float[]{1,0,0,0,
                                     0,1,0,0,
                                     0,0,1,0,
                                     0,0,0,1};
            Assert.Equal (expect, (float[])m);
        }


        [Fact]
        public void Test_Transpose () {
            var m = new Matrix4x4 (1, 2, 3, 4,
                                   5, 6, 7, 8,
                                   9, 10, 11, 12,
                                   13, 14, 15, 16);
            var expected = new float[]{1,5,9,13,
                                        2,6,10,14,
                                        3,7,11,15,
                                        4,8,12,16};

            Assert.Equal (expected, (float[])m.Transpose ());
        }

        [Fact]
        public void Test_Inverse () {
            var m = new Matrix4x4 (1, 0, 1, 1,
                                   2, 1, 1, 0,
                                   0, -1, 2, -1,
                                   1, 1, 1, 0);
            var expected = new float[]{0, 1, 0, -1,
                                        -0.25f, -0.5f, -0.25f, 1.25f,
                                        0.25f, -0.5f, 0.25f, 0.75f,
                                        0.75f, -0.5f, -0.25f, 0.25f};

            Assert.Equal (expected, (float[])m.Inverse ());
        }

        [Fact]
        public void Test_Translate () {
            var m = Matrix4x4.Translate (1, 2, 3);
            var expected = new float[]{1,0,0,1,
                                      0,1,0,2,
                                      0,0,1,3,
                                      0,0,0,1};
            Assert.Equal (expected, (float[])m);
        }

        [Fact]
        public void Test_Rotate () {
            var rotX90 = Matrix4x4.Rotate (90, 1, 0, 0);
            var rotY90 = Matrix4x4.Rotate (90, 0, 1, 0);
            var rotZ90 = Matrix4x4.Rotate (90, 0, 0, 1);

            Assert.Equal ((float[])Vector4.Back, (float[])(rotX90 * Vector4.Up), new FloatComparer (0.0001f));
            Assert.Equal ((float[])Vector4.Left, (float[])(rotY90 * Vector4.Forward), new FloatComparer(0.0001f));
            Assert.Equal ((float[])Vector4.Right, (float[])(rotZ90 * Vector4.Down), new FloatComparer(0.0001f));
        }

        [Fact]
        public void Test_Scale () {
            var m = Matrix4x4.Scale (1, 2, 3);
            var expected = new float[]{1,0,0,0,
                                     0,2,0,0,
                                     0,0,3,0,
                                     0,0,0,1};
            Assert.Equal (expected, (float[])m);
        }

        [Fact]
        public void Test_MatrixVector () {
            var m = new Matrix4x4 (1, 0, 1, 1,
                                   2, 1, 1, 0,
                                   0, -1, 2, -1,
                                   1, 1, 1, 0);
            var v = new Vector4 (1, 2, 3, 1);
            var expected = new Vector4 (5, 7, 3, 6);

            Assert.Equal (expected, m * v);
        }

        [Fact]
        public void Test_MatrixMatrix () {
            var m1 = new Matrix4x4 (1, 2, 3, 4,
                                   5, 6, 7, 8,
                                   9, 10, 11, 12,
                                   13, 14, 15, 16);
            var m2 = new Matrix4x4 (17, 18, 19, 20,
                                    21, 22, 23, 24,
                                    25, 26, 27, 28,
                                    29, 30, 31, 32);
            var expected = new Matrix4x4 (  250,     260,     270,     280,   
                                            618,     644,     670,     696,   
                                             986,     1028,    1070,    1112,  
                                            1354,    1412,    1470,    1528  );
            Assert.Equal (expected, m1 * m2);
 
        }

        [Fact]
        public void Test_Equals () {
            var m1 = new Matrix4x4 (0, 1, 2, 3,
                                    4, 5, 6, 7,
                                    8, 9, 10, 11,
                                    12, 13, 14, 15.0f);
            var m2 = new Matrix4x4 (0, 1, 2, 3, 
                                    4, 5, 6, 7, 
                                    8, 9, 10, 11,
                                    12, 13, 14, 15.02f);
            var tmp = GlobalSettings.Tolerance;

            GlobalSettings.Tolerance = 0.1f;

            Assert.True (m1 == m2);
            Assert.True (m1.Equals (m2));
            Assert.True (m1.Equals ((object)m2));
            Assert.False (m1 != m2);

            GlobalSettings.Tolerance = 0.01f;

            Assert.False (m1 == m2);
            Assert.False (m1.Equals (m2));
            Assert.False (m1.Equals ((object)m2));
            Assert.True (m1 != m2);

            GlobalSettings.Tolerance = tmp;
        }
    }
}
